﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Xml;

namespace Common.XmlHandle
{
    public class XmlData
    {
        private XmlDocument xdoc = new XmlDocument();
        private XmlNode root = null;
        private string xmlPath = null;
        static ReaderWriterLockSlim lockSlim = new ReaderWriterLockSlim();

        public XmlData(string path)
        {
            xmlPath = path;
            try
            {
                xdoc = new XmlDocument();
                xdoc.Load(path);
                root = xdoc.SelectSingleNode("Data");
            }
            catch (DirectoryNotFoundException)
            {
                Directory.CreateDirectory(path.Substring(0, path.LastIndexOf(@"\")));
                XmlDeclaration dec = xdoc.CreateXmlDeclaration("1.0", "utf-8", null);
                xdoc.AppendChild(dec);
                xdoc.AppendChild(xdoc.CreateElement("Data"));
                root = xdoc.SelectSingleNode("Data");
                xdoc.Save(path);
            }
            catch (FileNotFoundException)
            {
                XmlDeclaration dec = xdoc.CreateXmlDeclaration("1.0", "utf-8", null);
                xdoc.AppendChild(dec);
                xdoc.AppendChild(xdoc.CreateElement("Data"));
                root = xdoc.SelectSingleNode("Data");
                xdoc.Save(path);
            }
            catch (XmlException)
            {
                throw;
                //xdoc = new XmlDocument();
                //XmlDeclaration dec = xdoc.CreateXmlDeclaration("1.0", "utf-8", null);
                //xdoc.AppendChild(dec);
                //xdoc.AppendChild(xdoc.CreateElement("Data"));
                //root = xdoc.SelectSingleNode("Data");
                //xdoc.Save(path);
            }
            catch
            {
                throw;
            }
        }

        public ClassData GetDataByID(string year, int id)
        {
            lockSlim.EnterReadLock();
            
            //xdoc.Load(xmlPath);
            //root = xdoc.SelectSingleNode("Data");
            XmlNode rot = root.SelectSingleNode("Data" + year);

            ClassData b = new ClassData();
            XmlNodeList nodes = rot.ChildNodes;
            foreach (XmlNode node in nodes)
            {
                XmlElement nod = (XmlElement)node;
                if (Int32.Parse(nod.GetAttribute("ID")) == id)
                {
                    b = getData(node);
                    lockSlim.ExitReadLock();
                    return b;
                }
            }
            lockSlim.ExitReadLock();
            return null;
        }

        public bool CheckData(ClassData data)
        {
            lockSlim.EnterReadLock();
            //xdoc.Load(xmlPath);
            //root = xdoc.SelectSingleNode("Data");
            XmlNode rot = root.SelectSingleNode("Data" + data.TimeArrange.Year);
            ClassData dat = GetDataByID(data.TimeArrange.Year.ToString(), data.ID);
            if (dat.IsEqual(data))
            {
                lockSlim.ExitReadLock();
                return true;
            }
            else
            {
                lockSlim.ExitReadLock();
                return false;
            }
        }
        
        public ClassData GetLastData()
        {
            lockSlim.EnterReadLock();
            //xdoc.Load(xmlPath);
            //root = xdoc.SelectSingleNode("Data");
            ClassData max = null;
            try
            {
                XmlNode rot = root.SelectSingleNode("Data" + DateTime.Now.Year.ToString());
                int count = rot.ChildNodes.Count;
                max = getData(rot.FirstChild);
                for (int i = 0; i < count; i++)
                {
                    ClassData data = getData(rot.ChildNodes[i]);
                    if (data.ID > max.ID)
                    {
                        max.Clone(data);
                    }
                }
            }
            catch { }
            finally
            {
                lockSlim.ExitReadLock();
            }
            return max;
        }

        public int GetLastID()
        {
            ClassData data = GetLastData();
            if (data == null)
            {
                return 0;
            }
            else
            {
                return data.ID;
            }
        }

        public List<ClassData> GetAllData()
        {
            lockSlim.EnterReadLock();
            //xdoc.Load(xmlPath);
            //root = xdoc.SelectSingleNode("Data");
            List<ClassData> b = new List<ClassData>(100);
            foreach (XmlNode rot in root.ChildNodes)
            {
                XmlNodeList nodes = rot.ChildNodes;
                foreach (XmlNode node in nodes)
                {
                    b.Add(getData(node));
                }
            }
            lockSlim.ExitReadLock();
            return b;
        }
        
        ///////////////////////////////////////////////////////////////////////////////////
        ///////////////////////////XML文件Code（编码）的操作///////////////////////////////
        ///////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// 获取data.xml文件中指定编码的list
        /// </summary>
        /// <param name="year">指定年份</param>
        /// <param name="code">指定编码 例：SXXAGS20160101</param>
        /// <returns></returns>
        private List<ClassData> GetCodes(string year, string code)
        {
            //lockSlim.EnterReadLock();
            //xdoc.Load(xmlPath);
            //root = xdoc.SelectSingleNode("Data");
            List<ClassData> b = new List<ClassData>();
            try
            {
                XmlNode rot = root.SelectSingleNode("Data" + year);
                XmlNodeList nodes = rot.ChildNodes;
                foreach (XmlNode node in nodes)
                {
                    XmlElement nod = (XmlElement)node;
                    string xcode = nod.GetAttribute("Code");
                    if (xcode.IndexOf(code) > -1)
                    {
                        b.Add(getData(node));
                    }
                }
                //lockSlim.ExitReadLock();
                return b;
            }
            catch
            {
                //lockSlim.ExitReadLock();
                return null;
            }
        }

        public int GetCodeNumber(string year, string code)
        {
            lockSlim.EnterReadLock();
            //xdoc.Load(xmlPath);
            //root = xdoc.SelectSingleNode("Data");
            List<ClassData> list = new List<ClassData>();
            int yearNow = Int32.Parse(DateTime.Now.ToString().Substring(0, 4));
            int yearHappen = Int32.Parse(year);
            for (int i = 0; i <= yearNow - yearHappen; i++)
            {
                string yearr = (yearHappen + i).ToString();
                if (GetCodes(yearr, code) == null)
                {
                    continue;
                }
                foreach (ClassData dat in GetCodes(yearr, code))
                {
                    list.Add(dat);
                }
            }
            lockSlim.ExitReadLock();
            return list.Count;
        }

        public void AddData(ClassData data)
        {
            //lockSlim.EnterWriteLock();
            //防止读写锁干涉，先进行读取
            int lastID = GetLastID();
            //再进行写操作
            lockSlim.TryEnterWriteLock(-1);
            //xdoc.Load(xmlPath);
            //root = xdoc.SelectSingleNode("Data");
            try
            {
                if (root.ChildNodes == null)//如果本年数据是空的
                {
                    XmlElement rot = xdoc.CreateElement("Data" + DateTime.Now.Year.ToString());
                    root.AppendChild(rot);
                    AddDataNode(rot as XmlNode, data);
                    xdoc.Save(xmlPath);
                }
                else
                {
                    XmlNode rot = root.SelectSingleNode("Data" + DateTime.Now.Year.ToString());
                    if (rot == null)
                    {
                        XmlElement rt = xdoc.CreateElement("Data" + DateTime.Now.Year.ToString());
                        root.AppendChild(rt);
                        rot = rt;
                    }
                    if (lastID >= data.ID)
                    {
                        XmlNodeList nodes = rot.ChildNodes;
                        foreach (XmlNode node in nodes)
                        {
                            XmlElement nod = (XmlElement)node;
                            if (Int32.Parse(nod.GetAttribute("ID")) == data.ID)
                            {
                                editNode(node, data);
                            }
                        }
                    }
                    else
                    {
                        AddDataNode(rot, data);
                    }
                }
                //lockSlim.ExitWriteLock();
            }
            finally
            {
                xdoc.Save(xmlPath);
                lockSlim.ExitWriteLock();
            }
        }

        public void EditData(ClassData classData)
        {
            lockSlim.EnterWriteLock();
            //xdoc.Load(xmlPath);
            //root = xdoc.SelectSingleNode("Data");
            //XmlNode xmlNode = root.ChildNodes[classData.ID - 1];
            XmlNode xmlNode = root.SelectSingleNode("Data" + classData.TimeArrange.Year);
            editNode(xmlNode, classData);
            xdoc.Save(xmlPath);
            lockSlim.ExitWriteLock();
        }

        /// <summary>
        /// 删除最后一条数据，提供ID以供验证
        /// </summary>
        /// <param name="lastId"></param>
        public void RemoveLastData(int lastId)
        {
            lockSlim.EnterWriteLock();
            //xdoc.Load(xmlPath);
            //root = xdoc.SelectSingleNode("Data");
            XmlNode xmlNode = root.LastChild;
            XmlElement element = xmlNode as XmlElement;
            if(element.GetAttribute("ID") == lastId.ToString())
            {
                xmlNode.ParentNode.RemoveChild(xmlNode);
                xdoc.Save(xmlPath);
            }
            lockSlim.ExitWriteLock();
        }
        ////////////////////////////////////////////
        ////////////////基础操作方法////////////////
        ////////////////////////////////////////////
        private void editNode(XmlNode node, ClassData data)
        {
            if(node == null)
            {
                return;
            }
            else
            {
                node.Attributes["ID"].InnerText = data.ID.ToString();
                node.Attributes["Code"].InnerText = data.Code;
                XmlNode info = node.SelectSingleNode("Info");
                info.Attributes[0].InnerText = data.CaseType.ToString();
                info.Attributes[1].InnerText = data.Brigade.ToString();
                info.Attributes[2].InnerText = data.Contact.ToString();
                info.Attributes[3].InnerText = data.User.ToString();
                info.Attributes[4].InnerText = data.TimeHappen.ToString("yyyy/MM/dd HH:mm");
                info.Attributes[5].InnerText = data.TimeEntrust.ToString("yyyy/MM/dd");
                info.Attributes[6].InnerText = data.TimeArrange.ToString("yyyy/MM/dd HH:mm:ss");
                XmlNode vehicles = node.SelectSingleNode("Vehicles");
                XmlNodeList vehs = vehicles.ChildNodes;
                foreach(XmlNode veh in vehs)
                {
                    veh.RemoveAll();
                }
                foreach(ClassVehicle veh in data.Vehicles)
                {
                    XmlElement ele = xdoc.CreateElement("Vehicle");
                    ele.SetAttribute("Number", veh.Number);
                    ele.SetAttribute("Brand", veh.Brand);
                    ele.SetAttribute("Type", veh.Type);
                    string project = null;
                    if (veh.Procj) { project += "a"; };
                    if (veh.Procs) { project += "b"; };
                    if (veh.Prohj) { project += "c"; };
                    if (veh.Pross) { project += "d"; };
                    if (veh.Procy) { project += "e"; };
                    if (veh.Progc) { project += "f"; };
                    if (veh.Prosx) { project += "g"; };
					if (veh.Projsy) { project += "h"; };
					if (veh.Profx) { project += "i"; };
					if (project == null || project == "") { project = "x"; };
                    ele.SetAttribute("Project", project);
                    vehicles.AppendChild(ele);
                }
            }
        }

        private ClassData getData(XmlNode node)
        {
            if(node == null)
            {
                return null;
            }
            ClassData b = new ClassData();
            XmlElement xele = (XmlElement)node;
            b.ID = Int32.Parse(xele.GetAttribute("ID"));
            b.Code = xele.GetAttribute("Code");
            XmlNodeList nods = node.ChildNodes;
            XmlElement xel1 = (XmlElement)nods[0];
            b.CaseType = Int32.Parse(xel1.GetAttribute("CaseType"));
            b.Brigade = new Guid(xel1.GetAttribute("Brigade"));
            b.Contact = xel1.GetAttribute("Contact");
            b.User = new Guid(xel1.GetAttribute("User"));
            b.TimeHappen = DateTime.Parse(xel1.GetAttribute("TimeHappen"));
            b.TimeEntrust = DateTime.Parse(xel1.GetAttribute("TimeEntrust"));
            b.TimeArrange = DateTime.Parse(xel1.GetAttribute("TimeArrange"));
            XmlNodeList nos = nods[1].ChildNodes;
            foreach (XmlNode no in nos)
            {
                ClassVehicle veh = new ClassVehicle();
                XmlElement car = (XmlElement)no;
                veh.Number = car.GetAttribute("Number");
                veh.Brand = car.GetAttribute("Brand");
                veh.Type = car.GetAttribute("Type");
                string project = car.GetAttribute("Project");
                if (project == null || project == "" || project == "x") { b.Vehicles.Add(veh); continue; };
                if (project.IndexOf('a') >= 0) { veh.Procj = true; };
                if (project.IndexOf('b') >= 0) { veh.Procs = true; };
                if (project.IndexOf('c') >= 0) { veh.Prohj = true; };
                if (project.IndexOf('d') >= 0) { veh.Pross = true; };
                if (project.IndexOf('e') >= 0) { veh.Procy = true; };
                if (project.IndexOf('f') >= 0) { veh.Progc = true; };
                if (project.IndexOf('g') >= 0) { veh.Prosx = true; };
				if (project.IndexOf('h') >= 0) { veh.Projsy = true; };
				if (project.IndexOf('i') >= 0) { veh.Profx = true; };
				b.Vehicles.Add(veh);
            }
            return b;
        }

        private void AddDataNode(XmlNode root, ClassData data)
        {
            XmlElement node = xdoc.CreateElement("Case");
            node.SetAttribute("ID", data.ID.ToString());
            node.SetAttribute("Code", data.Code);
            root.AppendChild(node);
            XmlElement ele = xdoc.CreateElement("Info");
            ele.SetAttribute("CaseType", data.CaseType.ToString());
            ele.SetAttribute("Brigade", data.Brigade.ToString());
            ele.SetAttribute("Contact", data.Contact);
            ele.SetAttribute("User", data.User.ToString());
            ele.SetAttribute("TimeHappen", data.TimeHappen.ToString("yyyy/MM/dd HH:mm"));
            ele.SetAttribute("TimeEntrust", data.TimeEntrust.ToString("yyyy/MM/dd"));
            ele.SetAttribute("TimeArrange", data.TimeArrange.ToString("yyyy/MM/dd HH:mm:ss"));
            node.AppendChild(ele);
            XmlElement nod = xdoc.CreateElement("Vehicles");
            node.AppendChild(nod);
            foreach (ClassVehicle car in data.Vehicles)
            {
                if(car == null)
                {
                    continue;
                }
                if ((car.Number == null || car.Number == string.Empty) | 
                    (car.Brand == null || car.Brand == string.Empty) | 
                    (car.Type == null || car.Type == ""))
                {
                    continue;
                }
                XmlElement e = xdoc.CreateElement("Vehicle");
                e.SetAttribute("Number", car.Number);
                e.SetAttribute("Brand", car.Brand);
                e.SetAttribute("Type", car.Type);
                string project = null;
                if (car.Procj) { project += "a"; };
                if (car.Procs) { project += "b"; };
                if (car.Prohj) { project += "c"; };
                if (car.Pross) { project += "d"; };
                if (car.Procy) { project += "e"; };
                if (car.Progc) { project += "f"; };
                if (car.Prosx) { project += "g"; };
                if (car.Projsy) { project += "h"; };
                if (car.Profx) { project += "i"; };
                if (project == null || project == "") { project = "x"; };
                e.SetAttribute("Project", project);
                nod.AppendChild(e);
            }
        }

        private ClassVehicle Xml2Vehicle(XmlNode xmlNode)
        {
            ClassVehicle b = new ClassVehicle();
            XmlElement car = (XmlElement)xmlNode;
            b.Number = car.GetAttribute("Number");
            b.Brand = car.GetAttribute("Brand");
            b.Type = car.GetAttribute("Type");
            string project = car.GetAttribute("Project");
            if (project.IndexOf('a') >= 0) { b.Procj = true; };
            if (project.IndexOf('b') >= 0) { b.Procs = true; };
            if (project.IndexOf('c') >= 0) { b.Prohj = true; };
            if (project.IndexOf('d') >= 0) { b.Pross = true; };
            if (project.IndexOf('e') >= 0) { b.Procy = true; };
            if (project.IndexOf('f') >= 0) { b.Progc = true; };
            if (project.IndexOf('g') >= 0) { b.Prosx = true; };
            if (project.IndexOf('h') >= 0) { b.Projsy = true; };
            if (project.IndexOf('i') >= 0) { b.Profx = true; };
            return b;
        }
    }
}
